Jim Stackhouse
Graduate Student
Computing Sciences
Summer 1996
The C Programming Language
The History of C
Insight
Language Structure and
Semantics
Preprocessor
Directives
Data Types and
Variables
Functions
Operators
Keywords
The ANSI C Header Files
Aphabetic Index to ANSI C
Functions
ALGOL (1960's, One
of the first block-structured languages)
CPL
BCPL
(Martin Richards)
B (Ken Thompson,
Bell Labs)
C (Dennis Ritchie, Bell Labs)
Dennis Ritchie developed C
essentially to write operating systems. Ritchie and Thompson along with
co-worker Brian Kernighan, developed and rewrote the UNIX operating system
kernel using Ritchie's C compiler on a DEC PDP-11/20 in 1973. It flourished from
there, and today, C is the basis for object oriented languages like C++ and
Java.
Go to top
The C programming language is acknowledged
by many as being one of the most flexible and powerful languages for allowing a
programmer to interact with a particular computing platform and operating
system. This can occur on several levels of abstraction. By this I mean that
there exists a robustness in the available tools provided to the C programmer,
both in the form of libraries, as well as lower level elements of the language.
On the second point, C allows bit manipulation and flexible interpretation of
memory contents or variables through the use of pointers and type associations.
Few restrictions are placed on the programmer relative to other languages. While
providing tight and compact syntax for many operations, C can be quite
challenging to interpret. That is to say, C code may be easier to write than to
read. Much of this is up to the programmer's style.
Go to top
C is a block
structured programming language, consisting of one or more files making up the
project. Contained in a given file you will find:
- Preprocessor Directives
- Data
- Code
Preprocessor directives are not C statements, but
instructions for the C preprocessor, an entity that essentially expands macro
definitions, conceptually glues together separate files that you have specified
and can also hide sections of your program from the compiler using conditional
compilation switches and statements.
Data in C, consists fundamentally of bytes stored in memory, which are
not instructions for the cpu, but have some informational content for the
program or programmer. These bytes can have more than one interpretation by the
code referencing it depending on the context of the reference. The visibility or
scope of a particular data(variable) item, to all of the code in a users
program, can vary depending on it's physical placement in the program source
code file, and by it's definition. Data also has a lifetime characteristic. Some
variables are always in existance and others are created and destroyed
dynamically as the program/programmer needs them.
Code in C, represents the actual high-level instructions that you want
the processor to execute. You can have it interact with the user through devices
like the keyboard, screen or printer. The code can and will also interact with
different forms of storage, such as ram, rom and external disk, tape or optical
media. C is a block structured language, based on the use of code functions. A
function is a grouping of processor instructions, following defined rules as
defined by C. Functions are called, when their name appears in a C line of code.
They may have data passed to them in a pre-defined format, and can return a
result of a pre-defined type. Furthermore, several functions relating to a
common task, may be grouped together in a separate file for project organization
purposes, or for inclusion in some future project. There is one special purpose
function that all programs must have. It is the 'main()' function. This is the
starting point for the computer when it begins executing your programs code. You
may put whatever code or function calls you wish in 'main()'.
Go to top
#define name string :performs macros substitutions
#define TRUE 1
#define FALSE 0
#error message :compiler stops and displays line number and message
#include filename :compiler will also compile filename at this point
#include <stdio.h> searches standard include directory for stdio.h
#include "stdio.h" searches working directory for stdio.h
#if,#ifdef,#ifndef,#else,#elif,#endif
:selectively compiles enclosed code sections
based on specified condition
#if constant expression
C code...
#elif NOTE: #elif can only be used with #if blocks
C code...
#else
C code...
#endif
The following can also be used to start an #ifxxx block:
#ifdef name :if name is defined by previous #define
#ifndef name :if name is not defined by previous #define
Go to top
There are 5 basic built-in
data types in C. They are:
TYPE C KEYWORD
character char
integer int
floating point float
double floating point double
valueless void
All of these except for void, may be modified by the following
keywords:
TYPE MODIFIERS(prefixes)
signed
unsigned
short
long
Variables in C are case sensitive, so X and x are two different variables.
Variable names may contain several characters including underscores("_") to form
meaningful names. All variables must have some form of declaration before being
used. The compiler needs to know what data type you wish to have associated with
the memory contents for this variable, and appropriately allocate enough bytes
of storage for it. Declaring a variable takes the following form...
[modifier] type variable_name;
eg. unsigned int myinteger;
long int mylongint;
char mychar;
float myfloat;
Structures
A structure is a method for grouping several related
data types together, such as someones name and address. The elements of the
structure are referenced using the "." dot operator. If using a pointer to a
structure, than the desired element can be referenced using the "->"" arrow
operator, which dereferences the pointer to the structure. struct AddressRec {
char first[15];
char last[20];
int housenum;
char street[20];
} myaddr, *youraddr;
then...
myaddr.housenum :would be the house number.
youraddr = &myaddr; (assign pointer to existing myaddr struct)
youraddr->housenum :would also be the house number.
Unions
If you take the definition of a structure, but overlap
all the variables onto the same memory starting location, you end up with a
union. Unions allow one to reference data bytes in any form outlined in the
union's definition. The size of the union entity in memory will be that of the
longest type listed in the union. Again, the dot operator is used to select the
desired component. union multi {
char c;
int i;
float f;
} myunion;
then...
myunion.c :is the character representation
using the first byte
myunion.i :is the integer representation
using the first 2/4 bytes depends on system
myunion.f :is the floating point representation
using the first 4 bytes
Enumerations
An enumeration is a list of objects, which are
sequentially assigned integer values, unless you overide the value in the
definition. enum fruit {Apples, Oranges, Peaches}; :Apples=0, Oranges=1, Peaches=2
enum fruit myfavfruit;
then...
myfavfruit = Apples
Arrays
Arrays of a data type can be created using brackets after
the variable name definition, including the size or number of elements of that
type to allocate space for. The components of the array can then be accessed
using the bracket and subscript notation. The array variable name when
referenced without the brackets, refers to the address of the array in memory.
Arrays are zero-based, and therefore have a maximum subscript of one less than
the declared size number. int x[10]; :creates an array 0 to 9, of ints.
then...
x[3] :references the 4th integer in the array.
x[0] :references the 1st integer in the array.
Strings
Strings in C, are simply character arrays, with a
terminating null character, ascii '0'. This protocol for strings is accomodated
where appropriate in the library functions defined by C. When defining string
variables, space can be allocated and the terminating null character implicitly
applied for you as follows:
char mystring[] = "This is my string.";
Pointers
Pointers play a major role in C programming. Many
of the library functions require pointers to variables to be passed, so that
they can be directly read or manipulated. Variables declared to be pointers, in
C, are also required to be associated with a given data type, like pointer to
int, or pointer to char array. In this way, pointer arithmetic can be supported
by the C compiler, without the programmer needing to know the proper increment
value to advance to another position in memory, of a given data type. Adding one
to a float pointer, will actually cause the pointer to advance 4 bytes, the size
of a float. The * dereference and & address operators are used in dealing
with pointer types.
Storage Class Modifiers
These keywords are
used with variable declarations, to specify specific needs or conditions
associated with the storage of the variables in memory. extern :used to indicate a variable is
declared in another file
auto :variables declared inside
of functions get this by default
register :requests placement in a cpu register
for speed
const :variable may not be changed by
the program during execution
volatile :the variable may be affected/changed
by outside influences(clock, etc.)
static :keeps local variables in existance
for duration of program and maintains
its value, but restricts visibility
to block where it was defined.
Scope and Lifetime of Variables
The scope or visibility of a
variable is determined by it's placement within the C source code file. This,
along with modifiers above, also determine when and how long a variable will
exist for use within the program. Any variables declared outside of a function
are considered global, and can be used by any line of code below the line it was
declared. Typically, global variables are places at the top of the file. Local
variables, however are those which are declared within the block structure of
functions. They are created when the function is called and destroyed when the
function exits, unless the static keyword is used in the variables definition.
Go to top
The most important function definition in C
is the main() function. It is where program execution begins. When the program
completes, it can return an integer code to the calling program, usually the
operating system. The main function can be declared with arguments or without.
The arguments that are declared, give the programmer access to the number of,
and to the command line arguments passed to the program when it was executed. It
is typically declared like this: int main(int argc, char *argv[]) {
...your code here...
}
then...
argc :number of arguments
*argv[1] :first character of first cmdline argument,
null terminated
*argv[0] :is always the name of the program invoked,
null terminated
Other users defined functions are what make up the bulk of a C program,
along with the standard C library functions. Functions are usually given a
prototype line either at the top of the source file, or in a separate file
called a header file(.h extension). The header file, along with any other
preprocessor directives, is then #include'd at the top of the source file. The
prototype helps the compiler to resolve names, arguments and types associated
with a function. Here is simple function definition: int myadd(int a, int b) {
int tmp;
tmp = a + b;
return(tmp);
}
Function parameters in C are passed by value to the function. This means
that a copy of the variable being passed it pushed onto the stack for the
function to work with. In many instances the programmer will want the actual
variable being passed to be altered directly by the function. To accomplish
this, pointers to variables can be passed to the function, allowing it to be
directly referenced.
Go to
top
C provides a rich set of operators for
the programmers use. In order to avoid undesirable results from combinations of
these operators, the programmer must understand the rules of evaluation that C
uses. Operators are grouped into categories of precedence. An operator in an
expression, whose precedence is 1 will have it's arguments evaluated before any
other lower precedence operator. In addition, a rule of associativity is in
effect for operators, which determines the logical direction of argument
grouping around similar precedence operators. Here is a table which shows all of
C's operators along with their precedence, associativity and the class of the
operator.
Precedence |
Symbol |
Name |
Associativity |
Class |
1 |
( ) |
subexpression and function call |
left to right |
primary expressions and postfix operators |
|
[ ] |
array subscript |
|
|
|
-> |
structure pointer |
|
|
|
. |
structure member |
|
|
|
++ |
increment(postfix) |
|
|
|
- - |
decrement(postfix) |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
2 |
! |
logical negation |
right to left |
unary |
|
~ |
one's complement |
|
|
|
++ |
increment(prefix) |
|
|
|
- - |
decrement(prefix) |
|
|
|
- |
unary negation |
|
|
|
+ |
unary plus |
|
|
|
(type) |
type cast |
|
|
|
* |
pointer indirection |
|
|
|
& |
address of |
|
|
|
sizeof |
size of |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
3 |
* |
multiplication |
left to right |
multiplicative |
|
/ |
division |
|
|
|
% |
modulus(remainder) |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
4 |
+ |
addition |
left to right |
additive |
|
- |
subtraction |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
5 |
< < |
bitwise left shift |
left to right |
bitwise shift |
|
>> |
bitwise right shift |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
6 |
< |
less than |
left to right |
relational |
|
< = |
less than or equal |
|
|
|
> |
greater than |
|
|
|
>= |
greater than or equal |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
7 |
== |
equal test |
left to right |
equality |
|
!= |
not equal test |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
8 |
& |
bitwise AND |
left to right |
bitwise |
Precedence |
Symbol |
Name |
Associativity |
Class |
9 |
^ |
bitwise exclusive OR |
left to right |
bitwise |
Precedence |
Symbol |
Name |
Associativity |
Class |
10 |
| |
bitwise inclusive OR |
left to right |
bitwise |
Precedence |
Symbol |
Name |
Associativity |
Class |
11 |
&& |
logical AND |
left to right |
logical |
Precedence |
Symbol |
Name |
Associativity |
Class |
12 |
|| |
logical inclusive OR |
left to right |
logical |
Precedence |
Symbol |
Name |
Associativity |
Class |
13 |
?: |
conditional test |
right to left |
conditional |
Precedence |
Symbol |
Name |
Associativity |
Class |
14 |
= |
plain assignment |
right to left |
assignment |
|
+= |
add |
|
|
|
-= |
subtract |
|
|
|
*= |
multiply |
|
|
|
/= |
divide |
|
|
|
%= |
remainder |
|
|
|
< < = |
bit left shift |
|
|
|
>>= |
bit right shift |
|
|
|
&= |
bit AND |
|
|
|
^= |
bit exclusive OR |
|
|
|
|= |
bit inclusive OR |
|
|
Precedence |
Symbol |
Name |
Associativity |
Class |
15 |
, |
comma |
left to right |
sequence |
Go to top
The ANSI C standard is comprised of 32
keywords, or reserved words. Many compilers have extended and added to these for
different computing platforms. The keywords are as follows: auto break case char
const continue default do
double else enum extern
float for goto if
int long register return
short signed sizeof static
struct switch typedef union
unsigned void volatile while
auto is used to create temporary or local variables that are
created when entering a block and is destroyed on exit from the block. This is
the default and is usually not needed explicitly.
auto int j;
break is used to exit a do, for or
while loop, and to exit from a switch statement preventing
fallthrough to the next case item.
while (j>0) {
i++;
if (i==10) break;
}
case is part of the switch statement contstruct, and
specifies a condition to test. If true the statement block following the case
keyword testing true, is executed until a break statement is reached.
switch (i) {
case 1: i++;
break;
case 2: i--;
break;
default: i=0;
}
char is a data type used to declare character variables.
char ch;
const tells the compiler that the variable
following, may not be modified.
const char ch;
continue causes the rest of the code in a
loop to be skipped, and control immediately goes to the condition statement of
the loop to be tested for repeat.
while (j>0) {
if (j==7) continue;
j--;
}
default is used in switch statements at the end, below case
options, to tell the compiler to execute the code there if none of the case
conditions returned true.
switch (i) {
case 1: i++;
break;
case 2: i--;
break;
default: i=0;
}
do is part of a do ... while looping construct, that has the
test condition at the end of the loop.
do {
i++;
} while (i<10);
double is a data type specifier for double precision floating
point variables.
double mypaycheck;
else is part of an if construct, and specifies and
alternate path of code execution when the condition in the if part returns
false.
if (i>=0) {
i--;
}
else {
i++;
}
enum is a data type specifier, which creates sequentially
assigned labels, and has a tag name created for the set of labels listed, which
subsequently can be used to declare variables of that type.
enum fruit {apples,peaches,oranges};
enum fruit myfruit;
...
myfruit=peaches;
extern is a data type modifer used to indicate to the compiler
that the variable is defined in another source file, and will also be used in
the current source file.
extern int varfromotherfile;
float is a data type for declaring floating point
variables.
float icecreamcost;
for is part of an interation or looping construct that provides
an initialization statement, a condition statement for continuing the
iterations, and and increment statement for advancing a counter in most
instances.
for (j=1; j<=10; j++) {
sum += j;
}
goto allows program execution to jump to a labeled line in the
program. The label being a name followed by a colon.
goto mylabel;
...
label:
if part of a conditional construct, that checks the specified
condition. If true (non-zero), the first statement block immediately following
the if, is executed. If false(zero), an optional statement block beginning with
the keyword else will be executed.
if (k==0) {
k=4;
}
else {
k--;
}
int is a data type specifier used to declare integer value
variables.
int j;
long is a data type modifier used to declare double sized
integer variables.
long int k;
register is a declaration modifier that tells
the compiler to use a cpu register for speed, instead of slower ram for the
variable. This is typically done for implementing fast loop counter
variables.
register int loopcntr;
return causes execution to return to the function that called
this one, and provides a mechanism for passing a return value associated with
the function.
int add(int a, int b) {
return(a+b);
}
sizeof is a compile time operator that returns the length of a
variable in bytes.
int intsize;
intsize=sizeof(int);
signed is a data type modifier that indicates that the high bit
of a data type is to be interpreted as a sign bit, allowing negative and
positive number representations.
signed int sint;
short is a data type modifier that declares integers to be only
one byte long.
short int shint;
static is a data type modifier that is used tell the compiler
that a local variable is to have permanant storage, maintaining it's value
between calls to the function where it is defined.
static int j;
struct is a keyword used to create a data type called a
structure, which is a grouping of other variable types, sequentially stored in
memory and accessed through one structure variable using member operators.
struct demostruct {
int j;
char ch;
} mystruct;
switch is part of a multi-conditional statement tree, that
tests a condition against a series of cases, using the case keyword to delineate
the conditions. Upon locating a true condition, the statement block at that
location is executed. If none pass, then an optional default branch can receive
execution.
switch (i) {
case 1: i++;
break;
case 2: i--;
break;
default: i=0;
}
typedef is used to create new labels for existing data
types.
typedef float price;
price coat;
union is used to assign two or more variables to the same
memory location. It is syntactically similar to a struct statement.
union numunion {
short int si;
long int li;
float f;
} myunion;
unsigned is a data type modifier that tells the compiler to not
interpret the high bit as a sign bit. Numbers are restricted therefore to only
positive values.
unsigned int uint;
void is a type specifier used to tell the compiler that nothing
is returned by a given function. It is also used in declaring generic pointers,
having no type associated with them.
void *vptr;
volatile is a modifier used to tell the compiler that the
contents of a variable may change due to outside influences, such as a clock
tick updated by hardware interrupts.
volatile int clocktick;
while is part of a looping construct the repeats it's enclose
statements as long as the supplied condition evaluates to true.
while (i>0) {
sum+=i;
}
Go to top
assert.h Diagnostics
ctype.h Character Handling and Conversion
float.h Floating Point Support
limits.h Maximum and Minimum Limits
locale.h International Support
math.h Mathematics
setjmp.h Jumps
signal.h Signal Handling
stdarg.h Variable Number of Arguments
stddef.h Standard Definitions
stdio.h Input and Output Support
stdlib.h General Definitions and Utilities
string.h String Handling
time.h Time and Date Support
Go to top
Alphabetic Index to ANSI C Functions
A
index...
B
index...
C
index...
D
index...
E
index...
F
TYPE |
IDENTIFIER |
HEADER |
double |
fabs(double
x); |
math |
int |
fclose(FILE
*stream); |
stdio |
int |
feof(FILE
*stream); |
stdio |
int |
ferror(FILE
*stream); |
stdio |
int |
fflush(FILE
*stream); |
stdio |
int |
fgetc(FILE
*stream); |
stdio |
int |
fgetpos(FILE
*stream, fpos_t *pos); |
stdio |
char * |
fgets(char *s,
int n, FILE *stream); |
stdio |
|
FILE |
stdio |
double |
floor(double
x); |
math |
double |
fmod(double x,
double y); |
math |
FILE * |
fopen(char
*filename, char *mode); |
stdio |
|
fpos_t |
stdio |
int |
fprintf(FILE
*stream, char *format, ...); |
stdio |
int |
fputc(int c,
FILE *stream); |
stdio |
int |
fputs(char *s,
FILE *stream); |
stdio |
size_t |
fread(void
*ptr, size_t size, size_t nbr, FILE* stream); |
stdio |
void |
free(void
*ptr); |
stdlib |
FILE * |
freopen(char
*filename, char *mode, FILE *stream); |
stdio |
double |
frexp(double
value, int *exp); |
math |
int |
fscanf(FILE
*stream, char *format, ...); |
stdio |
int |
fseek(FILE
*stream, long offset, int whence); |
stdio |
int |
fsetpos(FILE
*stream, fpos_t *pos); |
stdio |
long |
ftell(FILE
*stream); |
stdio |
size_t |
fwrite(void
*ptr, size_t size, size_t nbr, FILE *stream); |
stdio |
index...
G
index...
H
TYPE |
IDENTIFIER |
HEADER |
|
HUGE_VAL |
math |
|
HUGE_VAL |
stdlib |
index...
I
index...
J
TYPE |
IDENTIFIER |
HEADER |
|
jmp_buf |
setjmp |
index...
K
L
index...
M
TYPE |
IDENTIFIER |
HEADER |
void * |
malloc(size_t
size); |
stdlib |
void * |
memchr(void
*s, int c, size_t n); |
stdlib |
int |
memcmp(void
*s1, void *s2, size_t n); |
stdlib |
void * |
memcpy(void
*s1, void *s2, size_t nbr); |
stdlib |
void * |
memmove(void
*s1, void *s2, size_t nbr); |
stdlib |
void * |
memset(void
*s, int c, size_t n); |
stdlib |
time_t |
mktime(struct
tm *timeptr); |
time |
double |
modf(double
value, double *iptr); |
math |
index...
N
TYPE |
IDENTIFIER |
HEADER |
|
NDEBUG |
assert |
|
NULL |
stddef |
index...
O
TYPE |
IDENTIFIER |
HEADER |
|
offsetof(structure, member) |
stddef |
|
OPEN_MAX |
stdio |
index...
P
index...
Q
index...
R
index...
S
TYPE |
IDENTIFIER |
HEADER |
int |
scanf(char
*format, ...); |
stdio |
|
SEEK_CUR |
stdio |
|
SEEK_END |
stdio |
|
SEEK_SET |
stdio |
void |
setbuf(FILE
*stream, char *buf); |
stdio |
int |
setjmp(jmp_buf
env); |
setjmp |
char * |
setlocale(int
category, char *locale); |
locale |
int |
setvbuf(FILE
*stream, char *buf, int mode, size_t size); |
stdio |
|
sig_atomic_t |
signal |
|
SIG_DFL |
signal |
|
SIG_ERR |
signal |
|
SIG_IGN |
signal |
|
SIGABRT |
signal |
|
SIGFPE |
signal |
|
SIGILL |
signal |
|
SIGINT |
signal |
void |
(*signal(int
sig, void(*func)(int)))(int); |
signal |
|
SIGSEGV |
signal |
|
SIGTERM |
signal |
double |
sin(double
x); |
math |
double |
sinh(double
x); |
math |
|
size_t |
stddef |
int |
sprintf(char
*s, char *format, ...); |
stdio |
double |
sqrt(double
x); |
math |
void |
srand(unsigned
seed); |
stdlib |
int |
sscanf(char
*s, char *format, ...); |
stdio |
|
stderr |
stdio |
|
stdin |
stdio |
|
stdout |
stdio |
char * |
strcat(char
*s1,char *s2); |
stdlib |
char * |
strchr(char
*s, int c); |
stdlib |
int |
strcmp(char
*s1, char *s2); |
stdlib |
size_t |
strcoll(char
*to, size_t maxsize, char *from); |
stdlib |
char * |
strcpy(char
*s1, char *s2); |
stdlib |
size_t |
strcspn(char
*s1, char *s2); |
stdlib |
char * |
strerror(int
errnum); |
stdlib |
size_t |
strftime(char
*s, size_t maxsize, char *format, struct tm *timeptr); |
time |
size_t |
strlen(char
*s); |
stdilb |
char * |
strncat(char
*s1, char *s2, size_T n); |
stdlib |
int |
strncmp(char
*s1, char *s2, size_t n); |
stdlib |
char * |
strncpy(char
*s1, char *s2, size_ n); |
stdlib |
char * |
strpbrk(char
*s1, char *s2); |
stdlib |
char * |
strrchr(char
*s1, int c); |
stdlib |
size_t |
strspn(char
*s1, char *s2); |
stdlib |
char * |
strstr(char
*s1, char *s2); |
stdlib |
double |
strtod(char
*nptr, char **endptr); |
stdlib |
char * |
strtok(char
*s1, char *s2); |
stdlib |
long |
strtol(char
*nptr, char **endptr, int base); |
stdlib |
unsigned long |
strtoul(char
*nptr, char **endptr, int base); |
stdlib |
int |
system(char
*string); |
stdlib |
index...
T
index...
U
index...
V
index...
W
X
Y
Z
0-9
TYPE |
IDENTIFIER |
HEADER |
|
_IOFBF |
stdio |
|
_IOLBF |
stdio |
|
_IONBF |
stdio |
index...
The following texts were consulted in preparing this web page:
Defenbaugh, JR., Smedley, Richard, C Through Design: An Introduction
to ANSI C, Franklin, Beedle & Associates, 1988.
Schildt, Herbert,
C: The Pocket Reference, Osborne McGraw-Hill, 1988.
Kernhigan, Brian W.,
Pike, Rob, The UNIX Programming Environment, Prentice Hall Software
Series, 1984.
Borland C++
Programmers Guide, Borland International, 1991,92.
Also,
see...
What is
ANSI C?
Selected help on ANSI
C
C
Standard Library
Programming in
C
Computers and
Internet:Languages:C/C+
Douglass Mauro's 'C'
Programming Links
Jim Stackhouse
jstackho@monet.vill.edu
Created
Summer 1996